package springiesSimulator;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import element.Mass;
import element.Spring;


/**
 * Simulates objects moving around in a bounded environment.
 *
 * @author Robert C. Duvall
 */
public class Assembly {
    private List<Mass> myMasses;
    private List<Spring> mySprings;
    private Canvas myContainer;
    private EnvironmentManager myEnvironmentManager;
    private Point2D myCenterOfMass;

    /**
     * Create a Canvas with the given size.
     *
     * @param container canvas to create the viewer of the assemblies
     * @param environmentManager environmental conditions (gravity, viscosity,
     *        etc.)
     */
    public Assembly (Canvas container, EnvironmentManager environmentManager) {
        myMasses = new ArrayList<Mass>();
        mySprings = new ArrayList<Spring>();
        myContainer = container;
        myEnvironmentManager = environmentManager;
    }

    /**
     * @param mass assembly part that is effected by gravity
     */
    public void add (Mass mass) {
        myMasses.add(mass);
    }

    /**
     * @param spring connects two masses and acts on each of them
     */
    public void add (Spring spring) {
        mySprings.add(spring);
    }

    /**
     * Paint all shapes on the canvas.
     *
     * @param pen used to paint shape on the screen
     */
    public void paint (Graphics2D pen) {
        for (Spring s : mySprings) {
            s.paint(pen);
        }
        for (Mass m : myMasses) {
            m.paint(pen);
        }
    }

    /**
     * Called by each step of timer, multiple times per second.
     *
     * This should update the state of the animated shapes by just
     * a little so they appear to move over time.
     *
     * @param dt change in time, used to update movers
     */
    public void update (double dt) {
        updateCenterOfMass();
        for (Spring s : mySprings) {
            s.update(this, dt);
        }
        for (Mass m : myMasses) {
            m.update(this, dt);
        }
    }

    private void updateCenterOfMass () {
        if (myMasses.isEmpty()) { return; }

        int xMass = 0;
        int yMass = 0;
        int totalMass = 0;

        for (Mass m : myMasses) {
            Point2D center = m.getCenter();
            double mass = m.getMyMass();

            xMass += center.getX() * mass;
            yMass += center.getY() * mass;
            totalMass += mass;
        }

        setMyCenterOfMass(xMass / totalMass, yMass / totalMass);
    }

    /**
     * Returns size (in pixels) of the game area.
     */
    public Dimension getSize () {
        return myContainer.getSize();
    }

    /**
     * @param id unique number given to identify specific mass
     */
    public Mass getMass (int id) {
        for (Mass m : myMasses) {
            if (m.matchByID(id)) { return m; }
        }
        return null;
    }

    /**
     * @return the environmental conditions of the simulation
     */
    public EnvironmentManager getMyEnvironmentManager () {
        return myEnvironmentManager;
    }

    /**
     *
     * @return the canvas containing the assemblies
     */
    public Canvas getCanvas () {
        return myContainer;
    }

    /**
     *
     * @return center of mass
     */
    public Point2D getMyCenterOfMass () {
        return myCenterOfMass;
    }

    private void setMyCenterOfMass (int x, int y) {
        setMyCenterOfMass(new Point2D.Double(x, y));
    }

    private void setMyCenterOfMass (Point2D centerOfMass) {
        myCenterOfMass = centerOfMass;
    }

    /**
     *
     * @return list of masses in the assembly
     */
    public List<Mass> getMyMasses () {
        return myMasses;
    }

    /**
     *
     * @return list of spring-like components in the assembly
     */
    public List<Spring> getMySprings () {
        return mySprings;
    }
}
